home *** CD-ROM | disk | FTP | other *** search
/ Programming a Multiplayer FPS in DirectX / Programming a Multiplayer FPS in DirectX (Companion CD).iso / DirectX / dxsdk_oct2004.exe / dxsdk.exe / Samples / C++ / DirectInput / MultiMapper / multimapper.cpp < prev    next >
Encoding:
C/C++ Source or Header  |  2004-09-27  |  51.6 KB  |  1,360 lines

  1. //-----------------------------------------------------------------------------
  2. // File: MultiMapper.cpp
  3. //
  4. // Desc: This is a simple sample to demonstrate how to code using the DInput
  5. //       action mapper feature.
  6. //
  7. //-----------------------------------------------------------------------------
  8. #define STRICT
  9. #define DIRECTINPUT_VERSION 0x0800
  10.  
  11. #include <windows.h>
  12. #include <commctrl.h>
  13. #include <basetsd.h>
  14. #include <math.h>
  15. #include <stdio.h>
  16. #include <dxerr9.h>
  17. #include <tchar.h>
  18. #include <dinput.h>
  19. #include "MultiDI.h"
  20. #include "resource.h"
  21.  
  22.  
  23.  
  24.  
  25. //-----------------------------------------------------------------------------
  26. // Defines, and constants
  27. //-----------------------------------------------------------------------------
  28. #define SAMPLE_KEY        TEXT("Software\\Microsoft\\DirectX SDK\\MultiMapper")
  29.  
  30. // This GUID must be unique for every game, and the same for 
  31. // every instance of this app.  // {67131584-2938-4857-8A2E-D99DC2C82068}
  32. // The GUID allows DirectInput to remember input settings
  33. GUID g_guidApp = { 0x67131584, 0x2938, 0x4857, { 0x8a, 0x2e, 0xd9, 0x9d, 0xc2, 0xc8, 0x20, 0x68 } };
  34.  
  35.  
  36. // DirectInput action mapper reports events only when buttons/axis change
  37. // so we need to remember the present state of relevant axis/buttons for 
  38. // each DirectInput device.  The CMultiplayerInputDeviceManager will store a 
  39. // pointer for each device that points to this struct
  40. struct InputDeviceState
  41. {
  42.     FLOAT fAxisMoveUD;
  43.     BOOL  bButtonForwardThrust;
  44.     BOOL  bButtonReverseThrust;
  45.  
  46.     FLOAT fAxisRotateLR;
  47.     BOOL  bButtonRotateLeft;
  48.     BOOL  bButtonRotateRight;
  49.  
  50.     BOOL  bButtonFireWeapons;
  51.     BOOL  bButtonEnableShield;
  52. };
  53.  
  54.  
  55. // Struct to store the current input state
  56. struct UserInput
  57. {
  58.     FLOAT fAxisMoveUD;
  59.     FLOAT fAxisRotateLR;
  60.     BOOL  bButtonFireWeapons;
  61.     BOOL  bButtonEnableShield;
  62.  
  63.     BOOL  bDoConfigureInput;
  64.     BOOL  bDoQuitGame;
  65. };
  66.  
  67.  
  68. // Input semantics used by this app
  69. enum INPUT_SEMANTICS
  70. {
  71.     // Gameplay semantics
  72.     INPUT_ROTATE_AXIS_LR=1,  INPUT_MOVE_AXIS_UD,       
  73.     INPUT_FIREWEAPONS,       INPUT_ENABLESHIELD,    
  74.     INPUT_TURNLEFT,          INPUT_TURNRIGHT, 
  75.     INPUT_FORWARDTHRUST,     INPUT_REVERSETHRUST,
  76.     INPUT_DISPLAYGAMEMENU,   INPUT_QUITGAME,
  77. };
  78.  
  79. // Actions used by this app
  80. DIACTION g_rgGameAction[] =
  81. {
  82.     // If DirectInput has already been given an action map for this GUID, it
  83.     // will have created a user map for this application 
  84.     // (C:\Program Files\Common Files\DirectX\DirectInput\User Maps\*.ini). If a user map
  85.     // exists, DirectInput will use the action map defined in the stored user 
  86.     // map instead of the map defined in your program. This allows the user to
  87.     // customize controls without losing changes when the game restarts. If you 
  88.     // wish to make changes to the default action map without changing the 
  89.     // GUID, you will need to delete the stored user map from your hard drive
  90.     // for the system to detect your changes and recreate a stored user map.
  91.     
  92.     // Device input (joystick, etc.) that is pre-defined by DInput, according
  93.     // to genre type. The genre for this app is space simulators.
  94.     { INPUT_ROTATE_AXIS_LR,  DIAXIS_SPACESIM_LATERAL,      0, TEXT("Turn left/right"), },
  95.     { INPUT_MOVE_AXIS_UD,    DIAXIS_SPACESIM_MOVE,         0, TEXT("Move"), },
  96.     { INPUT_FIREWEAPONS,     DIBUTTON_SPACESIM_FIRE,       0, TEXT("Shoot"), },
  97.     { INPUT_ENABLESHIELD,    DIBUTTON_SPACESIM_GEAR,       0, TEXT("Shield"), },
  98.     { INPUT_DISPLAYGAMEMENU, DIBUTTON_SPACESIM_DISPLAY,    0, TEXT("Configure"), },
  99.  
  100.     // Keyboard input mappings
  101.     { INPUT_TURNLEFT,        DIKEYBOARD_LEFT,    0, TEXT("Turn left"), },
  102.     { INPUT_TURNRIGHT,       DIKEYBOARD_RIGHT,   0, TEXT("Turn right"), },
  103.     { INPUT_FORWARDTHRUST,   DIKEYBOARD_UP,      0, TEXT("Move forward"), },
  104.     { INPUT_REVERSETHRUST,   DIKEYBOARD_DOWN,    0, TEXT("Move backward"), },
  105.     { INPUT_FIREWEAPONS,     DIKEYBOARD_F,       0, TEXT("Fire weapons"), },
  106.     { INPUT_ENABLESHIELD,    DIKEYBOARD_S,       0, TEXT("Enable shields"), },
  107.     { INPUT_DISPLAYGAMEMENU, DIKEYBOARD_D,       DIA_APPFIXED, TEXT("Configure"), },
  108.     { INPUT_QUITGAME,        DIKEYBOARD_ESCAPE,  DIA_APPFIXED, TEXT("Quit game"), },
  109.  
  110.     // Mouse input mappings
  111.     { INPUT_ROTATE_AXIS_LR,  DIMOUSE_XAXIS,      0, _T("Turn left/right"), },
  112.     { INPUT_MOVE_AXIS_UD,    DIMOUSE_YAXIS,      0, _T("Move"), },
  113.     { INPUT_FIREWEAPONS,     DIMOUSE_BUTTON0,    0, _T("Shoot"), },
  114.     { INPUT_ENABLESHIELD,    DIMOUSE_BUTTON1,    0, _T("Enable shields"), },
  115. };
  116.  
  117. #define NUMBER_OF_GAMEACTIONS    (sizeof(g_rgGameAction)/sizeof(DIACTION))
  118.  
  119.  
  120.  
  121.  
  122. //-----------------------------------------------------------------------------
  123. // Function prototypes 
  124. //-----------------------------------------------------------------------------
  125. INT_PTR CALLBACK StaticMsgProc( HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam );
  126.  
  127.  
  128.  
  129.  
  130. //-----------------------------------------------------------------------------
  131. // Name: class CMyApplication 
  132. // Desc: Application class.
  133. //-----------------------------------------------------------------------------
  134. class CMyApplication 
  135. {
  136.     TCHAR*                  m_strWindowTitle;       // Title for the app's window
  137.     HWND                    m_hWnd;                 // The main app window
  138.     FLOAT                   m_fTime;                // Current time in seconds
  139.     FLOAT                   m_fElapsedTime;         // Time elapsed since last frame
  140.  
  141.     CMultiplayerInputDeviceManager* m_pInputDeviceManager;  // DirectInput multiplayer device manager
  142.     DIACTIONFORMAT          m_diafGame;             // Action format for game play
  143.     UserInput               m_UserInput[4];         // Struct for storing user input 
  144.  
  145.     DWORD                   m_dwNumPlayers;         // Number of players in the game
  146.     FLOAT                   m_fWorldRotX;           // World rotation state X-axis
  147.     FLOAT                   m_fWorldRotY;           // World rotation state Y-axis
  148.  
  149. protected:
  150.     HRESULT OneTimeSceneInit();
  151.     HRESULT Render();
  152.     HRESULT FrameMove();
  153.     HRESULT FinalCleanup();
  154.  
  155.     HRESULT EnableUI();
  156.     HRESULT RenderText();
  157.     
  158.     HRESULT InitInput();
  159.     HRESULT ChangeNumPlayers( DWORD dwNumPlayers, BOOL bResetOwnership, BOOL bResetMappings );
  160.     VOID    ConfigInput();
  161.     void    UpdateInput();
  162.     void    CleanupDirectInput();
  163.     void    CleanupDeviceStateStructs();
  164.         
  165. public:
  166.     HRESULT Create( HINSTANCE hInstance );
  167.     INT     Run();
  168.     INT_PTR MsgProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam );
  169.  
  170.     CMyApplication();
  171.  
  172.     HRESULT InputAddDeviceCB( CMultiplayerInputDeviceManager::PlayerInfo* pPlayerInfo, CMultiplayerInputDeviceManager::DeviceInfo* pDeviceInfo, const DIDEVICEINSTANCE* pdidi );
  173.     static HRESULT CALLBACK StaticInputAddDeviceCB( CMultiplayerInputDeviceManager::PlayerInfo* pPlayerInfo, CMultiplayerInputDeviceManager::DeviceInfo* pDeviceInfo, const DIDEVICEINSTANCE* pdidi, LPVOID pParam );   
  174. };
  175.  
  176.  
  177.  
  178.  
  179. //-----------------------------------------------------------------------------
  180. // Global access to the app (needed for the global WndProc())
  181. //-----------------------------------------------------------------------------
  182. CMyApplication*    g_pApp  = NULL;
  183. HINSTANCE          g_hInst = NULL;
  184.  
  185.  
  186.  
  187.  
  188. //-----------------------------------------------------------------------------
  189. // Name: WinMain()
  190. // Desc: Application entry point
  191. //-----------------------------------------------------------------------------
  192. INT WINAPI WinMain( HINSTANCE hInstance, HINSTANCE, LPSTR, int nCmdShow )
  193. {
  194.     CMyApplication app;
  195.  
  196.     g_hInst = hInstance;
  197.  
  198.     InitCommonControls();
  199.  
  200.     if( FAILED( app.Create( hInstance ) ) )
  201.         return 0;
  202.  
  203.     return app.Run();
  204. }
  205.  
  206.  
  207.  
  208.  
  209. //-----------------------------------------------------------------------------
  210. // Name: CMyApplication()
  211. // Desc: Constructor
  212. //-----------------------------------------------------------------------------
  213. CMyApplication::CMyApplication()
  214. {
  215.     g_pApp                      = this;
  216.     m_hWnd                      = NULL;
  217.     m_strWindowTitle            = TEXT( "DirectInput MultiMapper Sample" );
  218.     m_pInputDeviceManager       = NULL;
  219.  
  220.     ZeroMemory( m_UserInput, sizeof(UserInput)*4 );
  221.     m_fWorldRotX                = 0.0f;
  222.     m_fWorldRotY                = 0.0f;
  223.     m_dwNumPlayers              = 1;
  224. }
  225.  
  226.  
  227.  
  228.  
  229. //-----------------------------------------------------------------------------
  230. // Name: Create()
  231. // Desc: Creates the window
  232. //-----------------------------------------------------------------------------
  233. HRESULT CMyApplication::Create( HINSTANCE hInstance )
  234. {
  235.     // Display the main dialog box.
  236.     CreateDialog( hInstance, MAKEINTRESOURCE(IDD_MAIN), 
  237.                   NULL, StaticMsgProc );
  238.     if( NULL == m_hWnd )
  239.         return E_FAIL;
  240.  
  241.     // Initialize the application timer
  242.     DXUtil_Timer( TIMER_START );
  243.  
  244.     return S_OK;
  245. }
  246.  
  247.  
  248.  
  249.  
  250. //-----------------------------------------------------------------------------
  251. // Name: Run()
  252. // Desc: Handles the message loop and calls FrameMove() and Render() when
  253. //       idle.
  254. //-----------------------------------------------------------------------------
  255. INT CMyApplication::Run()
  256. {
  257.     MSG msg;
  258.  
  259.     // Message loop to run the app
  260.     for( ; ; )
  261.     {
  262.         if( PeekMessage( &msg, NULL, 0, 0, PM_REMOVE ) )
  263.         {
  264.             // Skip WM_KEYDOWN so they aren't handled by the dialog
  265.             if( msg.message == WM_KEYDOWN )
  266.                 continue;
  267.  
  268.             if( !IsDialogMessage( m_hWnd, &msg ) )  
  269.             {
  270.                 TranslateMessage( &msg );
  271.                 DispatchMessage( &msg );
  272.             }
  273.  
  274.             if( msg.message == WM_QUIT )
  275.             {
  276.                 DestroyWindow( m_hWnd );
  277.                 break;
  278.             }
  279.         }
  280.         else
  281.         {
  282.             // Update the time variables
  283.             m_fTime        = DXUtil_Timer( TIMER_GETAPPTIME );
  284.             m_fElapsedTime = DXUtil_Timer( TIMER_GETELAPSEDTIME );
  285.  
  286.             // This app uses idle time processing for the game loop
  287.             if( FAILED( FrameMove() ) )
  288.                 SendMessage( m_hWnd, WM_DESTROY, 0, 0 );
  289.             if( FAILED( Render() ) ) 
  290.                 SendMessage( m_hWnd, WM_DESTROY, 0, 0 );
  291.  
  292.             Sleep( 20 );
  293.         }
  294.     }
  295.  
  296.     FinalCleanup();
  297.  
  298.     return (INT)msg.wParam;
  299. }
  300.  
  301.  
  302.  
  303.  
  304. //-----------------------------------------------------------------------------
  305. // Name: OneTimeSceneInit()
  306. // Desc: Called during initial app startup, this function performs all the
  307. //       permanent initialization.
  308. //-----------------------------------------------------------------------------
  309. HRESULT CMyApplication::OneTimeSceneInit()
  310. {
  311.     HRESULT hr;
  312.     
  313.     // Load the icon
  314.     HICON hIcon = LoadIcon( GetModuleHandle(NULL), MAKEINTRESOURCE( IDI_MAIN ) );
  315.     
  316.     // Set the icon for this dialog.
  317.     SendMessage( m_hWnd, WM_SETICON, ICON_BIG,   (LPARAM) hIcon );  // Set big icon
  318.     SendMessage( m_hWnd, WM_SETICON, ICON_SMALL, (LPARAM) hIcon );  // Set small icon
  319.     
  320.     // Initialize DirectInput
  321.     if( FAILED( hr = InitInput() ) )
  322.         return DXTRACE_ERR_MSGBOX( TEXT("InitInput"), hr );
  323.     
  324.     HWND hNumPlayers = GetDlgItem( m_hWnd, IDC_NUM_PLAYERS_COMBO );
  325.     SendMessage( hNumPlayers, CB_ADDSTRING, 0, (LPARAM) TEXT("1") );
  326.     SendMessage( hNumPlayers, CB_ADDSTRING, 0, (LPARAM) TEXT("2") );
  327.     SendMessage( hNumPlayers, CB_ADDSTRING, 0, (LPARAM) TEXT("3") );
  328.     SendMessage( hNumPlayers, CB_ADDSTRING, 0, (LPARAM) TEXT("4") );
  329.     SendMessage( hNumPlayers, CB_SETCURSEL, m_dwNumPlayers-1, 0 );
  330.  
  331.     EnableUI();
  332.     
  333.     return S_OK;
  334. }
  335.  
  336.  
  337.  
  338.  
  339. //-----------------------------------------------------------------------------
  340. // Name: InitInput()
  341. // Desc: Initialize DirectInput objects
  342. //-----------------------------------------------------------------------------
  343. HRESULT CMyApplication::InitInput()
  344. {
  345.     HRESULT hr;
  346.  
  347.     // Setup action format for the actual gameplay
  348.     ZeroMemory( &m_diafGame, sizeof(DIACTIONFORMAT) );
  349.     m_diafGame.dwSize          = sizeof(DIACTIONFORMAT);
  350.     m_diafGame.dwActionSize    = sizeof(DIACTION);
  351.     m_diafGame.dwDataSize      = NUMBER_OF_GAMEACTIONS * sizeof(DWORD);
  352.     m_diafGame.guidActionMap   = g_guidApp;
  353.     m_diafGame.dwGenre         = DIVIRTUAL_SPACESIM; 
  354.     m_diafGame.dwNumActions    = NUMBER_OF_GAMEACTIONS;
  355.     m_diafGame.rgoAction       = g_rgGameAction;
  356.     m_diafGame.lAxisMin        = -100;
  357.     m_diafGame.lAxisMax        = 100;
  358.     m_diafGame.dwBufferSize    = 16;
  359.     _tcscpy( m_diafGame.tszActionMap, _T("MultiMapper Sample") );
  360.     
  361.     // Create a new input device manager
  362.     m_pInputDeviceManager = new CMultiplayerInputDeviceManager( SAMPLE_KEY );
  363.  
  364.     if( FAILED( hr = ChangeNumPlayers( 1, FALSE, FALSE ) ) )
  365.         return DXTRACE_ERR_MSGBOX( TEXT("ChangeNumPlayers"), hr );
  366.     
  367.     return S_OK;
  368. }
  369.  
  370.  
  371.  
  372.  
  373. //-----------------------------------------------------------------------------
  374. // Name: StaticInputAddDeviceCB()
  375. // Desc: Static callback helper to call into CMyApplication class
  376. //-----------------------------------------------------------------------------
  377. HRESULT CALLBACK CMyApplication::StaticInputAddDeviceCB( 
  378.                                          CMultiplayerInputDeviceManager::PlayerInfo* pPlayerInfo, 
  379.                                          CMultiplayerInputDeviceManager::DeviceInfo* pDeviceInfo, 
  380.                                          const DIDEVICEINSTANCE* pdidi, 
  381.                                          LPVOID pParam )
  382. {
  383.     CMyApplication* pApp = (CMyApplication*) pParam;
  384.     return pApp->InputAddDeviceCB( pPlayerInfo, pDeviceInfo, pdidi );
  385. }
  386.  
  387.  
  388.  
  389.  
  390. //-----------------------------------------------------------------------------
  391. // Name: InputAddDeviceCB()
  392. // Desc: Called from CMultiplayerInputDeviceManager whenever a device is added. 
  393. //       Set the dead zone, and creates a new InputDeviceState for each device
  394. //-----------------------------------------------------------------------------
  395. HRESULT CMyApplication::InputAddDeviceCB( CMultiplayerInputDeviceManager::PlayerInfo* pPlayerInfo, 
  396.                                          CMultiplayerInputDeviceManager::DeviceInfo* pDeviceInfo, 
  397.                                          const DIDEVICEINSTANCE* pdidi )
  398. {
  399.     if( (GET_DIDEVICE_TYPE(pdidi->dwDevType) != DI8DEVTYPE_KEYBOARD) &&
  400.         (GET_DIDEVICE_TYPE(pdidi->dwDevType) != DI8DEVTYPE_MOUSE) )
  401.     {
  402.         // Setup the deadzone 
  403.         DIPROPDWORD dipdw;
  404.         dipdw.diph.dwSize       = sizeof(DIPROPDWORD);
  405.         dipdw.diph.dwHeaderSize = sizeof(DIPROPHEADER);
  406.         dipdw.diph.dwObj        = 0;
  407.         dipdw.diph.dwHow        = DIPH_DEVICE;
  408.         dipdw.dwData            = 500;
  409.         pDeviceInfo->pdidDevice->SetProperty( DIPROP_DEADZONE, &dipdw.diph );
  410.     }
  411.     
  412.     // Create a new InputDeviceState for each device so the 
  413.     // app can record its state 
  414.     InputDeviceState* pNewInputDeviceState = new InputDeviceState;
  415.     ZeroMemory( pNewInputDeviceState, sizeof(InputDeviceState) );
  416.     pDeviceInfo->pParam = (LPVOID) pNewInputDeviceState;
  417.             
  418.     HWND hPlayerDevice = NULL;
  419.     switch( pPlayerInfo->dwPlayerIndex )
  420.     {
  421.     case 0:
  422.         hPlayerDevice = GetDlgItem( m_hWnd, IDC_DEVICE_ASSIGNED_P1 );
  423.         break;
  424.     case 1:
  425.         hPlayerDevice = GetDlgItem( m_hWnd, IDC_DEVICE_ASSIGNED_P2 );
  426.         break;
  427.     case 2:
  428.         hPlayerDevice = GetDlgItem( m_hWnd, IDC_DEVICE_ASSIGNED_P3 );
  429.         break;
  430.     case 3:
  431.         hPlayerDevice = GetDlgItem( m_hWnd, IDC_DEVICE_ASSIGNED_P4 );
  432.         break;
  433.     }
  434.  
  435.     SendMessage( hPlayerDevice, LB_ADDSTRING, 0, (LPARAM) pdidi->tszProductName );
  436.     
  437.     return S_OK;
  438. }
  439.  
  440.  
  441.  
  442.  
  443. //-----------------------------------------------------------------------------
  444. // Name: ChangeNumPlayers()
  445. // Desc: Signals a change in the number of players. It is also called to reset
  446. //       ownership and mapping data.
  447. //-----------------------------------------------------------------------------
  448. HRESULT CMyApplication::ChangeNumPlayers( DWORD dwNumPlayers, BOOL bResetOwnership, 
  449.                                           BOOL bResetMappings )
  450. {
  451.     HRESULT hr;
  452.     
  453.     m_dwNumPlayers = dwNumPlayers;
  454.  
  455.     // Just pass in stock names.  Real games may want to ask the user for a name, etc.
  456.     TCHAR* strUserNames[4] = { TEXT("Player 1"), TEXT("Player 2"), 
  457.                                TEXT("Player 3"), TEXT("Player 4") };
  458.  
  459.     BOOL bSuccess = FALSE;
  460.     while( !bSuccess )
  461.     {
  462.         // Update UI
  463.         EnableUI();    
  464.         SendMessage( GetDlgItem(m_hWnd,IDC_DEVICE_ASSIGNED_P1), LB_RESETCONTENT, 0, 0 );
  465.         SendMessage( GetDlgItem(m_hWnd,IDC_DEVICE_ASSIGNED_P2), LB_RESETCONTENT, 0, 0 );
  466.         SendMessage( GetDlgItem(m_hWnd,IDC_DEVICE_ASSIGNED_P3), LB_RESETCONTENT, 0, 0 );
  467.         SendMessage( GetDlgItem(m_hWnd,IDC_DEVICE_ASSIGNED_P4), LB_RESETCONTENT, 0, 0 );
  468.         
  469.         hr = m_pInputDeviceManager->Create( m_hWnd, strUserNames, m_dwNumPlayers, &m_diafGame, 
  470.                                             StaticInputAddDeviceCB, this, 
  471.                                             bResetOwnership, bResetMappings );
  472.  
  473.         if( FAILED(hr) )
  474.         {
  475.             switch( hr )
  476.             {
  477.                 case E_DIUTILERR_DEVICESTAKEN:
  478.                 {
  479.                     // It's possible that a single user could "own" too many devices for the other
  480.                     // players to get into the game. If so, we reinit the manager class to provide 
  481.                     // each user with a device that has a default configuration.
  482.                     bResetOwnership = TRUE;
  483.                     
  484.                     MessageBox( m_hWnd, TEXT("You have entered more users than there are suitable ")       \
  485.                                         TEXT("devices, or some users are claiming too many devices.\n") \
  486.                                         TEXT("Press OK to give each user a default device"), 
  487.                                         TEXT("Devices Are Taken"), MB_OK | MB_ICONEXCLAMATION );
  488.                     break;
  489.                 }
  490.                     
  491.                 case E_DIUTILERR_TOOMANYUSERS:
  492.                 {
  493.                     // Another common error is if more users are attempting to play than there are devices
  494.                     // attached to the machine. In this case, the number of players is automatically 
  495.                     // lowered to make playing the game possible. 
  496.                     DWORD dwNumDevices = m_pInputDeviceManager->GetNumDevices();
  497.                     m_dwNumPlayers = dwNumDevices;                    
  498.                     SendMessage( GetDlgItem( m_hWnd, IDC_NUM_PLAYERS_COMBO ), 
  499.                                  CB_SETCURSEL, m_dwNumPlayers-1, 0 );
  500.                                 
  501.                     TCHAR sz[256];
  502.                     wsprintf( sz, TEXT("There are not enough devices attached to the ")          \
  503.                                   TEXT("system for the number of users you entered.\nThe ")      \
  504.                                   TEXT("number of users has been automatically changed to ")     \
  505.                                   TEXT("%i (the number of devices available on the system)."),
  506.                                   m_dwNumPlayers );
  507.                     MessageBox( m_hWnd, sz, _T("Too Many Users"), MB_OK | MB_ICONEXCLAMATION );                    
  508.                     break;
  509.                 }
  510.  
  511.                 default:
  512.                     return DXTRACE_ERR_MSGBOX( TEXT("m_pInputDeviceManager->Create"), hr );
  513.                     break;
  514.             }
  515.             
  516.             m_pInputDeviceManager->Cleanup();                                
  517.         }
  518.         else
  519.         {
  520.             bSuccess = TRUE;
  521.         }
  522.     }
  523.     
  524.     return S_OK;
  525. }
  526.  
  527.  
  528.  
  529.  
  530. //-----------------------------------------------------------------------------
  531. // Name: FrameMove()
  532. // Desc: Called once per frame, the call is the entry point for animating
  533. //       the scene.
  534. //-----------------------------------------------------------------------------
  535. HRESULT CMyApplication::FrameMove()
  536. {
  537.     // Update user input state
  538.     UpdateInput();
  539.     
  540.     for( DWORD iPlayer=0; iPlayer<m_dwNumPlayers; iPlayer++ )
  541.     {       
  542.         // Respond to input
  543.         if( m_UserInput[iPlayer].bDoConfigureInput )
  544.         {
  545.             // One-shot per keypress
  546.             m_UserInput[iPlayer].bDoConfigureInput = FALSE;
  547.  
  548.             ConfigInput();
  549.         }
  550.     
  551.         // Update the world state according to user input
  552.         if( m_UserInput[iPlayer].fAxisMoveUD )
  553.             m_fWorldRotY += m_fElapsedTime * m_UserInput[iPlayer].fAxisMoveUD;
  554.     
  555.         if( m_UserInput[iPlayer].fAxisRotateLR )
  556.             m_fWorldRotX += m_fElapsedTime * m_UserInput[iPlayer].fAxisRotateLR;
  557.     }
  558.     
  559.     return S_OK;
  560. }
  561.  
  562.  
  563.  
  564.  
  565. //-----------------------------------------------------------------------------
  566. // Name: UpdateInput()
  567. // Desc: Update the user input.  Called once per frame 
  568. //-----------------------------------------------------------------------------
  569. void CMyApplication::UpdateInput()
  570. {
  571.     if( NULL == m_pInputDeviceManager )
  572.         return;
  573.     
  574.     CMultiplayerInputDeviceManager::DeviceInfo* pDeviceInfos;
  575.     DWORD dwNumDevices;
  576.  
  577.     // Get access to the list of semantically-mapped input devices
  578.     m_pInputDeviceManager->GetDevices( &pDeviceInfos, &dwNumDevices );
  579.  
  580.     // Loop through all devices and check game input
  581.     for( DWORD i=0; i<dwNumDevices; i++ )
  582.     {
  583.         // skip past any devices that aren't assigned, 
  584.         // since we don't care about them
  585.         if( pDeviceInfos[i].pPlayerInfo == NULL )
  586.             continue;
  587.  
  588.         DIDEVICEOBJECTDATA rgdod[10];
  589.         DWORD   dwItems = 10;
  590.         HRESULT hr;
  591.         LPDIRECTINPUTDEVICE8 pdidDevice = pDeviceInfos[i].pdidDevice;
  592.         InputDeviceState* pInputDeviceState = (InputDeviceState*) pDeviceInfos[i].pParam;
  593.         FLOAT fScale = 1.0f;
  594.  
  595.         if( pDeviceInfos[i].bRelativeAxis )
  596.         {
  597.             // For relative axis data, the action mapper only informs us when
  598.             // the delta data is non-zero, so we need to zero its state
  599.             // out each frame
  600.             pInputDeviceState->fAxisMoveUD   = 0.0f;
  601.             pInputDeviceState->fAxisRotateLR = 0.0f;      
  602.  
  603.             // Scale the relative axis data to make it more equal to 
  604.             // absolute joystick data
  605.             fScale = 5.0f;
  606.         }
  607.  
  608.         hr = pdidDevice->Acquire();
  609.         hr = pdidDevice->Poll();
  610.         hr = pdidDevice->GetDeviceData( sizeof(DIDEVICEOBJECTDATA),
  611.                                         rgdod, &dwItems, 0 );
  612.         if( FAILED(hr) )
  613.             continue;
  614.  
  615.         // Get the sematics codes for the game menu
  616.         for( DWORD j=0; j<dwItems; j++ )
  617.         {
  618.             BOOL  bButtonState = (rgdod[j].dwData==0x80) ? TRUE : FALSE;
  619.             FLOAT fAxisState   = (FLOAT)((int)rgdod[j].dwData)/100.0f * fScale;
  620.  
  621.             switch( rgdod[j].uAppData )
  622.             {
  623.                 // Handle relative axis data
  624.                 case INPUT_ROTATE_AXIS_LR: 
  625.                     pInputDeviceState->fAxisRotateLR = fAxisState;
  626.                     break;
  627.                 case INPUT_MOVE_AXIS_UD:
  628.                     pInputDeviceState->fAxisMoveUD = -fAxisState;
  629.                     break;
  630.  
  631.                 // Handle buttons separately so the button state data
  632.                 // doesn't overwrite the axis state data, and handle
  633.                 // each button separately so they don't overwrite each other
  634.                 case INPUT_TURNLEFT:        pInputDeviceState->bButtonRotateLeft    = bButtonState; break;
  635.                 case INPUT_TURNRIGHT:       pInputDeviceState->bButtonRotateRight   = bButtonState; break;
  636.                 case INPUT_FORWARDTHRUST:   pInputDeviceState->bButtonForwardThrust = bButtonState; break;
  637.                 case INPUT_REVERSETHRUST:   pInputDeviceState->bButtonReverseThrust = bButtonState; break;
  638.                 case INPUT_FIREWEAPONS:     pInputDeviceState->bButtonFireWeapons   = bButtonState; break;
  639.                 case INPUT_ENABLESHIELD:    pInputDeviceState->bButtonEnableShield  = bButtonState; break;
  640.  
  641.                 // Handle one-shot buttons
  642.                 case INPUT_DISPLAYGAMEMENU: if( bButtonState ) m_UserInput[0].bDoConfigureInput = TRUE; break;
  643.                 case INPUT_QUITGAME:        if( bButtonState ) m_UserInput[0].bDoQuitGame       = TRUE; break;
  644.             }
  645.         }
  646.     }
  647.  
  648.     for( DWORD iPlayer=0; iPlayer<m_dwNumPlayers; iPlayer++ )
  649.     {       
  650.         // Process user input and store result into pUserInput struct
  651.         m_UserInput[iPlayer].fAxisRotateLR = 0.0f;
  652.         m_UserInput[iPlayer].fAxisMoveUD   = 0.0f;
  653.         m_UserInput[iPlayer].bButtonFireWeapons  = FALSE;
  654.         m_UserInput[iPlayer].bButtonEnableShield = FALSE;
  655.  
  656.         // Concatinate the data from all the DirectInput devices
  657.         for( i=0; i<dwNumDevices; i++ )
  658.         {
  659.             // Only look at devices that are assigned to this player 
  660.             if( pDeviceInfos[i].pPlayerInfo == NULL || 
  661.                 pDeviceInfos[i].pPlayerInfo->dwPlayerIndex != iPlayer )
  662.                 continue;
  663.  
  664.             InputDeviceState* pInputDeviceState = (InputDeviceState*) pDeviceInfos[i].pParam;
  665.  
  666.             // Use the axis data that is furthest from zero
  667.             if( fabs(pInputDeviceState->fAxisRotateLR) > fabs(m_UserInput[iPlayer].fAxisRotateLR) )
  668.                 m_UserInput[iPlayer].fAxisRotateLR = pInputDeviceState->fAxisRotateLR;
  669.  
  670.             if( fabs(pInputDeviceState->fAxisMoveUD) > fabs(m_UserInput[iPlayer].fAxisMoveUD) )
  671.                 m_UserInput[iPlayer].fAxisMoveUD = pInputDeviceState->fAxisMoveUD;
  672.  
  673.             // Process the button data 
  674.             if( pInputDeviceState->bButtonRotateLeft )
  675.                 m_UserInput[iPlayer].fAxisRotateLR = -1.0f;
  676.             else if( pInputDeviceState->bButtonRotateRight )
  677.                 m_UserInput[iPlayer].fAxisRotateLR = 1.0f;
  678.  
  679.             if( pInputDeviceState->bButtonForwardThrust )
  680.                 m_UserInput[iPlayer].fAxisMoveUD = 1.0f;
  681.             else if( pInputDeviceState->bButtonReverseThrust )
  682.                 m_UserInput[iPlayer].fAxisMoveUD = -1.0f;
  683.  
  684.             if( pInputDeviceState->bButtonFireWeapons )
  685.                 m_UserInput[iPlayer].bButtonFireWeapons = TRUE;
  686.             if( pInputDeviceState->bButtonEnableShield )
  687.                 m_UserInput[iPlayer].bButtonEnableShield = TRUE;
  688.         } 
  689.     }
  690. }
  691.  
  692.  
  693.  
  694.  
  695. //-----------------------------------------------------------------------------
  696. // Name: Render()
  697. // Desc: Called once per frame, the call is the entry point for rendering the 
  698. //       world.
  699. //-----------------------------------------------------------------------------
  700. HRESULT CMyApplication::Render()
  701. {
  702.     TCHAR szMsg[MAX_PATH];
  703.     TCHAR szMsgCurrent[MAX_PATH];
  704.  
  705.     // Update Player 1
  706.     _stprintf( szMsg, TEXT("%0.2f"), m_UserInput[0].fAxisMoveUD );
  707.     GetWindowText( GetDlgItem(m_hWnd,IDC_UD_AXIS_STATE_P1), szMsgCurrent, MAX_PATH );
  708.     if( lstrcmp( szMsgCurrent, szMsg ) != 0 )
  709.         SetWindowText( GetDlgItem(m_hWnd,IDC_UD_AXIS_STATE_P1), szMsg );
  710.     _stprintf( szMsg, TEXT("%0.2f"), m_UserInput[0].fAxisRotateLR );
  711.     GetWindowText( GetDlgItem(m_hWnd,IDC_LR_AXIS_STATE_P1), szMsgCurrent, MAX_PATH );
  712.     if( lstrcmp( szMsgCurrent, szMsg ) != 0 )
  713.         SetWindowText( GetDlgItem(m_hWnd,IDC_LR_AXIS_STATE_P1), szMsg );
  714.     if( !m_UserInput[0].bButtonEnableShield && !m_UserInput[0].bButtonFireWeapons )
  715.         _stprintf( szMsg, TEXT("None") );
  716.     else
  717.         _stprintf( szMsg, TEXT("%s%s"), m_UserInput[0].bButtonEnableShield ? TEXT("Shield ") : TEXT(""), 
  718.                                       m_UserInput[0].bButtonFireWeapons ? TEXT("Fire ") : TEXT("") );
  719.     GetWindowText( GetDlgItem(m_hWnd,IDC_BUTTON_STATE_P1), szMsgCurrent, MAX_PATH );
  720.     if( lstrcmp( szMsgCurrent, szMsg ) != 0 )
  721.         SetWindowText( GetDlgItem(m_hWnd,IDC_BUTTON_STATE_P1), szMsg );
  722.  
  723.     // Update Player 2
  724.     _stprintf( szMsg, TEXT("%0.2f"), m_UserInput[1].fAxisMoveUD );
  725.     GetWindowText( GetDlgItem(m_hWnd,IDC_UD_AXIS_STATE_P2), szMsgCurrent, MAX_PATH );
  726.     if( lstrcmp( szMsgCurrent, szMsg ) != 0 )
  727.         SetWindowText( GetDlgItem(m_hWnd,IDC_UD_AXIS_STATE_P2), szMsg );
  728.     _stprintf( szMsg, TEXT("%0.2f"), m_UserInput[1].fAxisRotateLR );
  729.     GetWindowText( GetDlgItem(m_hWnd,IDC_LR_AXIS_STATE_P2), szMsgCurrent, MAX_PATH );
  730.     if( lstrcmp( szMsgCurrent, szMsg ) != 0 )
  731.         SetWindowText( GetDlgItem(m_hWnd,IDC_LR_AXIS_STATE_P2), szMsg );
  732.     if( !m_UserInput[1].bButtonEnableShield && !m_UserInput[1].bButtonFireWeapons )
  733.         _stprintf( szMsg, TEXT("None") );
  734.     else
  735.         _stprintf( szMsg, TEXT("%s%s"), m_UserInput[1].bButtonEnableShield ? TEXT("Shield ") : TEXT(""), 
  736.         m_UserInput[1].bButtonFireWeapons ? TEXT("Fire ") : TEXT("") );
  737.     GetWindowText( GetDlgItem(m_hWnd,IDC_BUTTON_STATE_P2), szMsgCurrent, MAX_PATH );
  738.     if( lstrcmp( szMsgCurrent, szMsg ) != 0 )
  739.         SetWindowText( GetDlgItem(m_hWnd,IDC_BUTTON_STATE_P2), szMsg );
  740.     
  741.     // Update Player 3
  742.     _stprintf( szMsg, TEXT("%0.2f"), m_UserInput[2].fAxisMoveUD );
  743.     GetWindowText( GetDlgItem(m_hWnd,IDC_UD_AXIS_STATE_P3), szMsgCurrent, MAX_PATH );
  744.     if( lstrcmp( szMsgCurrent, szMsg ) != 0 )
  745.         SetWindowText( GetDlgItem(m_hWnd,IDC_UD_AXIS_STATE_P3), szMsg );
  746.     _stprintf( szMsg, TEXT("%0.2f"), m_UserInput[2].fAxisRotateLR );
  747.     GetWindowText( GetDlgItem(m_hWnd,IDC_LR_AXIS_STATE_P3), szMsgCurrent, MAX_PATH );
  748.     if( lstrcmp( szMsgCurrent, szMsg ) != 0 )
  749.         SetWindowText( GetDlgItem(m_hWnd,IDC_LR_AXIS_STATE_P3), szMsg );
  750.     if( !m_UserInput[2].bButtonEnableShield && !m_UserInput[2].bButtonFireWeapons )
  751.         _stprintf( szMsg, TEXT("None") );
  752.     else
  753.         _stprintf( szMsg, TEXT("%s%s"), m_UserInput[2].bButtonEnableShield ? TEXT("Shield ") : TEXT(""), 
  754.         m_UserInput[2].bButtonFireWeapons ? TEXT("Fire ") : TEXT("") );
  755.     GetWindowText( GetDlgItem(m_hWnd,IDC_BUTTON_STATE_P3), szMsgCurrent, MAX_PATH );
  756.     if( lstrcmp( szMsgCurrent, szMsg ) != 0 )
  757.         SetWindowText( GetDlgItem(m_hWnd,IDC_BUTTON_STATE_P3), szMsg );
  758.     
  759.     // Update Player 4
  760.     _stprintf( szMsg, TEXT("%0.2f"), m_UserInput[3].fAxisMoveUD );
  761.     GetWindowText( GetDlgItem(m_hWnd,IDC_UD_AXIS_STATE_P4), szMsgCurrent, MAX_PATH );
  762.     if( lstrcmp( szMsgCurrent, szMsg ) != 0 )
  763.         SetWindowText( GetDlgItem(m_hWnd,IDC_UD_AXIS_STATE_P4), szMsg );
  764.     _stprintf( szMsg, TEXT("%0.2f"), m_UserInput[3].fAxisRotateLR );
  765.     GetWindowText( GetDlgItem(m_hWnd,IDC_LR_AXIS_STATE_P4), szMsgCurrent, MAX_PATH );
  766.     if( lstrcmp( szMsgCurrent, szMsg ) != 0 )
  767.         SetWindowText( GetDlgItem(m_hWnd,IDC_LR_AXIS_STATE_P4), szMsg );
  768.     if( !m_UserInput[3].bButtonEnableShield && !m_UserInput[3].bButtonFireWeapons )
  769.         _stprintf( szMsg, TEXT("None") );
  770.     else
  771.         _stprintf( szMsg, TEXT("%s%s"), m_UserInput[3].bButtonEnableShield ? TEXT("Shield ") : TEXT(""), 
  772.         m_UserInput[3].bButtonFireWeapons ? TEXT("Fire ") : TEXT("") );
  773.     GetWindowText( GetDlgItem(m_hWnd,IDC_BUTTON_STATE_P4), szMsgCurrent, MAX_PATH );
  774.     if( lstrcmp( szMsgCurrent, szMsg ) != 0 )
  775.         SetWindowText( GetDlgItem(m_hWnd,IDC_BUTTON_STATE_P4), szMsg );      
  776.     
  777.     // Update world state
  778.     _stprintf( szMsg, TEXT("%0.3f, %0.3f"), m_fWorldRotX, m_fWorldRotY );
  779.     GetWindowText( GetDlgItem(m_hWnd,IDC_WORLD_STATE), szMsgCurrent, MAX_PATH );
  780.     if( lstrcmp( szMsgCurrent, szMsg ) != 0 )
  781.         SetWindowText( GetDlgItem(m_hWnd,IDC_WORLD_STATE), szMsg );
  782.  
  783.     return S_OK;
  784. }
  785.  
  786.  
  787.  
  788.  
  789. //-----------------------------------------------------------------------------
  790. // Name: StaticMsgProc()
  791. // Desc: Static msg handler which passes messages to the application class.
  792. //-----------------------------------------------------------------------------
  793. INT_PTR CALLBACK StaticMsgProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam )
  794. {
  795.     return g_pApp->MsgProc( hWnd, uMsg, wParam, lParam );
  796. }
  797.  
  798.  
  799.  
  800.  
  801. //-----------------------------------------------------------------------------
  802. // Name: MsgProc()
  803. // Desc: Callback for all Windows messages
  804. //-----------------------------------------------------------------------------
  805. INT_PTR CMyApplication::MsgProc( HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam )
  806. {
  807.     HRESULT hr;
  808.  
  809.     switch( msg )
  810.     {
  811.         case WM_INITDIALOG:
  812.             m_hWnd = hWnd;
  813.  
  814.             // Initialize the app's custom scene stuff
  815.             if( FAILED( hr = OneTimeSceneInit() ) )
  816.             {
  817.                 DXTRACE_ERR_MSGBOX( TEXT("OneTimeSceneInit"), hr );
  818.                 MessageBox( hWnd, TEXT("Error initializing DirectInput.  Sample will now exit."), 
  819.                             TEXT("DirectInput Sample"), MB_OK | MB_ICONERROR );
  820.                 PostQuitMessage( IDCANCEL );
  821.                 return TRUE;
  822.             }
  823.             break;
  824.             
  825.         case WM_COMMAND:
  826.         {
  827.             switch( LOWORD(wParam) )
  828.             {
  829.             case IDC_NUM_PLAYERS_COMBO:
  830.                 {
  831.                     if( HIWORD(wParam) == CBN_SELCHANGE )
  832.                     {
  833.                         TCHAR strItem[MAX_PATH];
  834.                         HWND hNumPlayers = GetDlgItem( m_hWnd, IDC_NUM_PLAYERS_COMBO );
  835.                     
  836.                         int nCurSel = (int) SendMessage( hNumPlayers, CB_GETCURSEL, 0, 0 );
  837.                         SendMessage( hNumPlayers, CB_GETLBTEXT, nCurSel, (LPARAM) strItem );
  838.                         DWORD dwNumPlayers = _ttoi( strItem );
  839.  
  840.                         if( FAILED( hr = ChangeNumPlayers( dwNumPlayers, FALSE, FALSE ) ) )
  841.                         {
  842.                             PostQuitMessage( IDCANCEL );                            
  843.                         }
  844.                     }
  845.                     break;
  846.                 }
  847.  
  848.                 case IDC_RESET_OWNERSHIP:
  849.                     ChangeNumPlayers( m_dwNumPlayers, TRUE, FALSE );
  850.                     break;
  851.                 
  852.                 case IDC_RESET_MAPPINGS:
  853.                     ChangeNumPlayers( m_dwNumPlayers, FALSE, TRUE );
  854.                     break;
  855.                 
  856.                 case IDCANCEL:
  857.                     PostQuitMessage( IDCANCEL );
  858.                     break;
  859.  
  860.                 case IDM_CONFIGINPUT:
  861.                     m_UserInput[0].bDoConfigureInput = TRUE;
  862.                     break;
  863.             }
  864.             break;
  865.         }
  866.     }
  867.     
  868.     return FALSE;
  869. }
  870.  
  871.  
  872.  
  873.  
  874. //-----------------------------------------------------------------------------
  875. // Name: FinalCleanup()
  876. // Desc: Called before the app exits, this function gives the app the chance
  877. //       to cleanup after itself.
  878. //-----------------------------------------------------------------------------
  879. HRESULT CMyApplication::FinalCleanup()
  880. {
  881.     // Cleanup DirectInput
  882.     CleanupDirectInput();
  883.     
  884.     return S_OK;
  885. }
  886.  
  887.  
  888.  
  889.  
  890. //-----------------------------------------------------------------------------
  891. // Name: CleanupDirectInput()
  892. // Desc: Cleanup DirectInput 
  893. //-----------------------------------------------------------------------------
  894. VOID CMyApplication::CleanupDirectInput()
  895. {
  896.     if( NULL == m_pInputDeviceManager )
  897.         return;
  898.     
  899.     CleanupDeviceStateStructs();
  900.     
  901.     // Cleanup DirectX input objects
  902.     SAFE_DELETE( m_pInputDeviceManager );
  903. }
  904.  
  905.  
  906.  
  907.  
  908. //-----------------------------------------------------------------------------
  909. // Name: CleanupDeviceStateStructs()
  910. // Desc: 
  911. //-----------------------------------------------------------------------------
  912. VOID CMyApplication::CleanupDeviceStateStructs()
  913. {
  914.     // Get access to the list of semantically-mapped input devices
  915.     // to delete all InputDeviceState structs before calling ConfigureDevices()
  916.     CMultiplayerInputDeviceManager::DeviceInfo* pDeviceInfos;
  917.     DWORD dwNumDevices;
  918.     
  919.     m_pInputDeviceManager->GetDevices( &pDeviceInfos, &dwNumDevices );
  920.  
  921.     for( DWORD i=0; i<dwNumDevices; i++ )
  922.     {
  923.         InputDeviceState* pInputDeviceState = (InputDeviceState*) pDeviceInfos[i].pParam;
  924.         SAFE_DELETE( pInputDeviceState );
  925.         pDeviceInfos[i].pParam = NULL;
  926.     }
  927. }
  928.  
  929.  
  930.  
  931.  
  932. //-----------------------------------------------------------------------------
  933. // Name: ConfigInput()
  934. // Desc: 
  935. //-----------------------------------------------------------------------------
  936. VOID CMyApplication::ConfigInput()
  937. {
  938.     HRESULT hr;
  939.     
  940.     EnableWindow( m_hWnd, FALSE );
  941.  
  942.     CleanupDeviceStateStructs();
  943.     
  944.     // Update UI
  945.     EnableUI();    
  946.     SendMessage( GetDlgItem(m_hWnd,IDC_DEVICE_ASSIGNED_P1), LB_RESETCONTENT, 0, 0 );
  947.     SendMessage( GetDlgItem(m_hWnd,IDC_DEVICE_ASSIGNED_P2), LB_RESETCONTENT, 0, 0 );
  948.     SendMessage( GetDlgItem(m_hWnd,IDC_DEVICE_ASSIGNED_P3), LB_RESETCONTENT, 0, 0 );
  949.     SendMessage( GetDlgItem(m_hWnd,IDC_DEVICE_ASSIGNED_P4), LB_RESETCONTENT, 0, 0 );
  950.     
  951.     // Configure the devices (with edit capability)
  952.     hr = m_pInputDeviceManager->ConfigureDevices( m_hWnd, NULL, NULL, DICD_EDIT, NULL );    
  953.     if( FAILED(hr) )
  954.     {
  955.         if( hr == E_DIUTILERR_PLAYERWITHOUTDEVICE )
  956.         {
  957.             // There's a player that hasn't been assigned a device.  Some games may
  958.             // want to handle this by reducing the number of players, or auto-assigning
  959.             // a device, or warning the user, etc.
  960.             MessageBox( m_hWnd, TEXT("There is at least one player that wasn't assigned ") \
  961.                                 TEXT("a device\n") \
  962.                                 TEXT("Press OK to auto-assign a device to these users"), 
  963.                                 TEXT("Player Without Device"), MB_OK | MB_ICONEXCLAMATION );
  964.         }
  965.  
  966.         // Auto-reassign every player a device.
  967.         ChangeNumPlayers( m_dwNumPlayers, FALSE, FALSE );
  968.     }
  969.  
  970.     EnableWindow( m_hWnd, TRUE );
  971.     SetForegroundWindow( m_hWnd );
  972. }
  973.  
  974.  
  975.  
  976.  
  977. //-----------------------------------------------------------------------------
  978. // Name: EnableUI()
  979. // Desc: 
  980. //-----------------------------------------------------------------------------
  981. HRESULT CMyApplication::EnableUI()
  982. {
  983.     BOOL bEnablePlayer4 = ( m_dwNumPlayers > 3 );
  984.     BOOL bEnablePlayer3 = ( m_dwNumPlayers > 2 );
  985.     BOOL bEnablePlayer2 = ( m_dwNumPlayers > 1 );
  986.  
  987.     // Enable/disable the player 2 ui
  988.     EnableWindow( GetDlgItem(m_hWnd,IDC_TEXT1_P2), bEnablePlayer2 );
  989.     EnableWindow( GetDlgItem(m_hWnd,IDC_TEXT2_P2), bEnablePlayer2 );
  990.     EnableWindow( GetDlgItem(m_hWnd,IDC_TEXT3_P2), bEnablePlayer2 );
  991.     EnableWindow( GetDlgItem(m_hWnd,IDC_PLAYER2_GROUP),    bEnablePlayer2 );
  992.     EnableWindow( GetDlgItem(m_hWnd,IDC_UD_AXIS_STATE_P2), bEnablePlayer2 );
  993.     EnableWindow( GetDlgItem(m_hWnd,IDC_LR_AXIS_STATE_P2), bEnablePlayer2 );
  994.     EnableWindow( GetDlgItem(m_hWnd,IDC_BUTTON_STATE_P2),  bEnablePlayer2 );
  995.     EnableWindow( GetDlgItem(m_hWnd,IDC_DEVICE_ASSIGNED_P2),  bEnablePlayer2 );
  996.     if( !bEnablePlayer2 )
  997.     {
  998.         SetWindowText( GetDlgItem(m_hWnd,IDC_UD_AXIS_STATE_P2), TEXT("n/a") );
  999.         SetWindowText( GetDlgItem(m_hWnd,IDC_LR_AXIS_STATE_P2), TEXT("n/a") );
  1000.         SetWindowText( GetDlgItem(m_hWnd,IDC_BUTTON_STATE_P2),  TEXT("n/a") );
  1001.     }
  1002.     
  1003.     // Enable/disable the player 3 ui
  1004.     EnableWindow( GetDlgItem(m_hWnd,IDC_TEXT1_P3), bEnablePlayer3 );
  1005.     EnableWindow( GetDlgItem(m_hWnd,IDC_TEXT2_P3), bEnablePlayer3 );
  1006.     EnableWindow( GetDlgItem(m_hWnd,IDC_TEXT3_P3), bEnablePlayer3 );
  1007.     EnableWindow( GetDlgItem(m_hWnd,IDC_PLAYER3_GROUP),    bEnablePlayer3 );
  1008.     EnableWindow( GetDlgItem(m_hWnd,IDC_UD_AXIS_STATE_P3), bEnablePlayer3 );
  1009.     EnableWindow( GetDlgItem(m_hWnd,IDC_LR_AXIS_STATE_P3), bEnablePlayer3 );
  1010.     EnableWindow( GetDlgItem(m_hWnd,IDC_BUTTON_STATE_P3),  bEnablePlayer3 );
  1011.     EnableWindow( GetDlgItem(m_hWnd,IDC_DEVICE_ASSIGNED_P3),  bEnablePlayer3 );
  1012.     if( !bEnablePlayer4 )
  1013.     {
  1014.         SetWindowText( GetDlgItem(m_hWnd,IDC_UD_AXIS_STATE_P3), TEXT("n/a") );
  1015.         SetWindowText( GetDlgItem(m_hWnd,IDC_LR_AXIS_STATE_P3), TEXT("n/a") );
  1016.         SetWindowText( GetDlgItem(m_hWnd,IDC_BUTTON_STATE_P3),  TEXT("n/a") );
  1017.     }
  1018.     
  1019.     // Enable/disable the player 4 ui
  1020.     EnableWindow( GetDlgItem(m_hWnd,IDC_TEXT1_P4), bEnablePlayer4 );
  1021.     EnableWindow( GetDlgItem(m_hWnd,IDC_TEXT2_P4), bEnablePlayer4 );
  1022.     EnableWindow( GetDlgItem(m_hWnd,IDC_TEXT3_P4), bEnablePlayer4 );
  1023.     EnableWindow( GetDlgItem(m_hWnd,IDC_PLAYER4_GROUP),    bEnablePlayer4 );
  1024.     EnableWindow( GetDlgItem(m_hWnd,IDC_UD_AXIS_STATE_P4), bEnablePlayer4 );
  1025.     EnableWindow( GetDlgItem(m_hWnd,IDC_LR_AXIS_STATE_P4), bEnablePlayer4 );
  1026.     EnableWindow( GetDlgItem(m_hWnd,IDC_BUTTON_STATE_P4),  bEnablePlayer4 );
  1027.     EnableWindow( GetDlgItem(m_hWnd,IDC_DEVICE_ASSIGNED_P4), bEnablePlayer4 );
  1028.     if( !bEnablePlayer4 )
  1029.     {
  1030.         SetWindowText( GetDlgItem(m_hWnd,IDC_UD_AXIS_STATE_P4), TEXT("n/a") );
  1031.         SetWindowText( GetDlgItem(m_hWnd,IDC_LR_AXIS_STATE_P4), TEXT("n/a") );
  1032.         SetWindowText( GetDlgItem(m_hWnd,IDC_BUTTON_STATE_P4),  TEXT("n/a") );
  1033.     }
  1034.     
  1035.     return S_OK;
  1036. }
  1037.  
  1038.  
  1039.  
  1040.  
  1041.  
  1042.  
  1043. //-----------------------------------------------------------------------------
  1044. // Name: DXUtil_Timer()
  1045. // Desc: Performs timer opertations. Use the following commands:
  1046. //          TIMER_RESET           - to reset the timer
  1047. //          TIMER_START           - to start the timer
  1048. //          TIMER_STOP            - to stop (or pause) the timer
  1049. //          TIMER_ADVANCE         - to advance the timer by 0.1 seconds
  1050. //          TIMER_GETABSOLUTETIME - to get the absolute system time
  1051. //          TIMER_GETAPPTIME      - to get the current time
  1052. //          TIMER_GETELAPSEDTIME  - to get the time that elapsed between 
  1053. //                                  TIMER_GETELAPSEDTIME calls
  1054. //-----------------------------------------------------------------------------
  1055. FLOAT __stdcall DXUtil_Timer( TIMER_COMMAND command )
  1056. {
  1057.     static BOOL     m_bTimerInitialized = FALSE;
  1058.     static BOOL     m_bUsingQPF         = FALSE;
  1059.     static BOOL     m_bTimerStopped     = TRUE;
  1060.     static LONGLONG m_llQPFTicksPerSec  = 0;
  1061.  
  1062.     // Initialize the timer
  1063.     if( FALSE == m_bTimerInitialized )
  1064.     {
  1065.         m_bTimerInitialized = TRUE;
  1066.  
  1067.         // Use QueryPerformanceFrequency() to get frequency of timer.  If QPF is
  1068.         // not supported, we will timeGetTime() which returns milliseconds.
  1069.         LARGE_INTEGER qwTicksPerSec;
  1070.         m_bUsingQPF = QueryPerformanceFrequency( &qwTicksPerSec );
  1071.         if( m_bUsingQPF )
  1072.             m_llQPFTicksPerSec = qwTicksPerSec.QuadPart;
  1073.     }
  1074.  
  1075.     if( m_bUsingQPF )
  1076.     {
  1077.         static LONGLONG m_llStopTime        = 0;
  1078.         static LONGLONG m_llLastElapsedTime = 0;
  1079.         static LONGLONG m_llBaseTime        = 0;
  1080.         double fTime;
  1081.         double fElapsedTime;
  1082.         LARGE_INTEGER qwTime;
  1083.         
  1084.         // Get either the current time or the stop time, depending
  1085.         // on whether we're stopped and what command was sent
  1086.         if( m_llStopTime != 0 && command != TIMER_START && command != TIMER_GETABSOLUTETIME)
  1087.             qwTime.QuadPart = m_llStopTime;
  1088.         else
  1089.             QueryPerformanceCounter( &qwTime );
  1090.  
  1091.         // Return the elapsed time
  1092.         if( command == TIMER_GETELAPSEDTIME )
  1093.         {
  1094.             fElapsedTime = (double) ( qwTime.QuadPart - m_llLastElapsedTime ) / (double) m_llQPFTicksPerSec;
  1095.             m_llLastElapsedTime = qwTime.QuadPart;
  1096.             return (FLOAT) fElapsedTime;
  1097.         }
  1098.     
  1099.         // Return the current time
  1100.         if( command == TIMER_GETAPPTIME )
  1101.         {
  1102.             double fAppTime = (double) ( qwTime.QuadPart - m_llBaseTime ) / (double) m_llQPFTicksPerSec;
  1103.             return (FLOAT) fAppTime;
  1104.         }
  1105.     
  1106.         // Reset the timer
  1107.         if( command == TIMER_RESET )
  1108.         {
  1109.             m_llBaseTime        = qwTime.QuadPart;
  1110.             m_llLastElapsedTime = qwTime.QuadPart;
  1111.             m_llStopTime        = 0;
  1112.             m_bTimerStopped     = FALSE;
  1113.             return 0.0f;
  1114.         }
  1115.     
  1116.         // Start the timer
  1117.         if( command == TIMER_START )
  1118.         {
  1119.             if( m_bTimerStopped )
  1120.                 m_llBaseTime += qwTime.QuadPart - m_llStopTime;
  1121.             m_llStopTime = 0;
  1122.             m_llLastElapsedTime = qwTime.QuadPart;
  1123.             m_bTimerStopped = FALSE;
  1124.             return 0.0f;
  1125.         }
  1126.     
  1127.         // Stop the timer
  1128.         if( command == TIMER_STOP )
  1129.         {
  1130.             if( !m_bTimerStopped )
  1131.             {
  1132.                 m_llStopTime = qwTime.QuadPart;
  1133.                 m_llLastElapsedTime = qwTime.QuadPart;
  1134.                 m_bTimerStopped = TRUE;
  1135.             }
  1136.             return 0.0f;
  1137.         }
  1138.     
  1139.         // Advance the timer by 1/10th second
  1140.         if( command == TIMER_ADVANCE )
  1141.         {
  1142.             m_llStopTime += m_llQPFTicksPerSec/10;
  1143.             return 0.0f;
  1144.         }
  1145.  
  1146.         if( command == TIMER_GETABSOLUTETIME )
  1147.         {
  1148.             fTime = qwTime.QuadPart / (double) m_llQPFTicksPerSec;
  1149.             return (FLOAT) fTime;
  1150.         }
  1151.  
  1152.         return -1.0f; // Invalid command specified
  1153.     }
  1154.     else
  1155.     {
  1156.         // Get the time using timeGetTime()
  1157.         static double m_fLastElapsedTime  = 0.0;
  1158.         static double m_fBaseTime         = 0.0;
  1159.         static double m_fStopTime         = 0.0;
  1160.         double fTime;
  1161.         double fElapsedTime;
  1162.         
  1163.         // Get either the current time or the stop time, depending
  1164.         // on whether we're stopped and what command was sent
  1165.         if( m_fStopTime != 0.0 && command != TIMER_START && command != TIMER_GETABSOLUTETIME)
  1166.             fTime = m_fStopTime;
  1167.         else
  1168.             fTime = GetTickCount() * 0.001;
  1169.     
  1170.         // Return the elapsed time
  1171.         if( command == TIMER_GETELAPSEDTIME )
  1172.         {   
  1173.             fElapsedTime = (double) (fTime - m_fLastElapsedTime);
  1174.             m_fLastElapsedTime = fTime;
  1175.             return (FLOAT) fElapsedTime;
  1176.         }
  1177.     
  1178.         // Return the current time
  1179.         if( command == TIMER_GETAPPTIME )
  1180.         {
  1181.             return (FLOAT) (fTime - m_fBaseTime);
  1182.         }
  1183.     
  1184.         // Reset the timer
  1185.         if( command == TIMER_RESET )
  1186.         {
  1187.             m_fBaseTime         = fTime;
  1188.             m_fLastElapsedTime  = fTime;
  1189.             m_fStopTime         = 0;
  1190.             m_bTimerStopped     = FALSE;
  1191.             return 0.0f;
  1192.         }
  1193.     
  1194.         // Start the timer
  1195.         if( command == TIMER_START )
  1196.         {
  1197.             if( m_bTimerStopped )
  1198.                 m_fBaseTime += fTime - m_fStopTime;
  1199.             m_fStopTime = 0.0f;
  1200.             m_fLastElapsedTime  = fTime;
  1201.             m_bTimerStopped = FALSE;
  1202.             return 0.0f;
  1203.         }
  1204.     
  1205.         // Stop the timer
  1206.         if( command == TIMER_STOP )
  1207.         {
  1208.             if( !m_bTimerStopped )
  1209.             {
  1210.                 m_fStopTime = fTime;
  1211.                 m_fLastElapsedTime  = fTime;
  1212.                 m_bTimerStopped = TRUE;
  1213.             }
  1214.             return 0.0f;
  1215.         }
  1216.     
  1217.         // Advance the timer by 1/10th second
  1218.         if( command == TIMER_ADVANCE )
  1219.         {
  1220.             m_fStopTime += 0.1f;
  1221.             return 0.0f;
  1222.         }
  1223.  
  1224.         if( command == TIMER_GETABSOLUTETIME )
  1225.         {
  1226.             return (FLOAT) fTime;
  1227.         }
  1228.  
  1229.         return -1.0f; // Invalid command specified
  1230.     }
  1231. }
  1232.  
  1233.  
  1234.  
  1235.  
  1236. //-----------------------------------------------------------------------------
  1237. // Name: DXUtil_ConvertGUIDToStringCch()
  1238. // Desc: Converts a GUID to a string 
  1239. //       cchDestChar is the size in TCHARs of strDest.  Be careful not to 
  1240. //       pass in sizeof(strDest) on UNICODE builds
  1241. //-----------------------------------------------------------------------------
  1242. HRESULT DXUtil_ConvertGUIDToStringCch( const GUID* pGuidSrc, TCHAR* strDest, int cchDestChar )
  1243. {
  1244.     int nResult = _sntprintf( strDest, cchDestChar, TEXT("{%0.8X-%0.4X-%0.4X-%0.2X%0.2X-%0.2X%0.2X%0.2X%0.2X%0.2X%0.2X}"),
  1245.                pGuidSrc->Data1, pGuidSrc->Data2, pGuidSrc->Data3,
  1246.                pGuidSrc->Data4[0], pGuidSrc->Data4[1],
  1247.                pGuidSrc->Data4[2], pGuidSrc->Data4[3],
  1248.                pGuidSrc->Data4[4], pGuidSrc->Data4[5],
  1249.                pGuidSrc->Data4[6], pGuidSrc->Data4[7] );
  1250.  
  1251.     if( nResult < 0 )
  1252.         return E_FAIL;
  1253.     return S_OK;
  1254. }
  1255.  
  1256.  
  1257. //-----------------------------------------------------------------------------
  1258. // Name: DXUtil_ConvertWideStringToAnsi()
  1259. // Desc: This is a UNICODE conversion utility to convert a WCHAR string into a
  1260. //       CHAR string. 
  1261. //       cchDestChar is the size in TCHARs of strDestination
  1262. //-----------------------------------------------------------------------------
  1263. HRESULT DXUtil_ConvertWideStringToAnsiCch( CHAR* strDestination, const WCHAR* wstrSource, 
  1264.                                      int cchDestChar )
  1265. {
  1266.     if( strDestination==NULL || wstrSource==NULL || cchDestChar < 1 )
  1267.         return E_INVALIDARG;
  1268.  
  1269.     int nResult = WideCharToMultiByte( CP_ACP, 0, wstrSource, -1, strDestination, 
  1270.                                        cchDestChar*sizeof(CHAR), NULL, NULL );
  1271.     strDestination[cchDestChar-1] = 0;
  1272.     
  1273.     if( nResult == 0 )
  1274.         return E_FAIL;
  1275.     return S_OK;
  1276. }
  1277.  
  1278.  
  1279. //-----------------------------------------------------------------------------
  1280. // Name: DXUtil_ConvertAnsiStringToGeneric()
  1281. // Desc: This is a UNICODE conversion utility to convert a WCHAR string into a
  1282. //       TCHAR string. 
  1283. //       cchDestChar is the size in TCHARs of tstrDestination.  Be careful not to 
  1284. //       pass in sizeof(strDest) on UNICODE builds
  1285. //-----------------------------------------------------------------------------
  1286. HRESULT DXUtil_ConvertWideStringToGenericCch( TCHAR* tstrDestination, const WCHAR* wstrSource, 
  1287.                                            int cchDestChar )
  1288. {
  1289.     if( tstrDestination==NULL || wstrSource==NULL || cchDestChar < 1 )
  1290.         return E_INVALIDARG;
  1291.  
  1292. #ifdef _UNICODE
  1293.     wcsncpy( tstrDestination, wstrSource, cchDestChar );
  1294.     tstrDestination[cchDestChar-1] = L'\0';    
  1295.     return S_OK;
  1296. #else
  1297.     return DXUtil_ConvertWideStringToAnsiCch( tstrDestination, wstrSource, cchDestChar );
  1298. #endif
  1299. }
  1300.  
  1301.  
  1302.  
  1303. //-----------------------------------------------------------------------------
  1304. // Name: DXUtil_ReadStringRegKeyCch()
  1305. // Desc: Helper function to read a registry key string
  1306. //       cchDest is the size in TCHARs of strDest.  Be careful not to 
  1307. //       pass in sizeof(strDest) on UNICODE builds.
  1308. //-----------------------------------------------------------------------------
  1309. HRESULT DXUtil_ReadStringRegKeyCch( HKEY hKey, LPCTSTR strRegName, TCHAR* strDest, 
  1310.                                     DWORD cchDest, LPCTSTR strDefault )
  1311. {
  1312.     DWORD dwType;
  1313.     DWORD cbDest = cchDest * sizeof(TCHAR);
  1314.  
  1315.     if( ERROR_SUCCESS != RegQueryValueEx( hKey, strRegName, 0, &dwType, 
  1316.                                           (BYTE*)strDest, &cbDest ) )
  1317.     {
  1318.         _tcsncpy( strDest, strDefault, cchDest );
  1319.         strDest[cchDest-1] = 0;
  1320.         return S_FALSE;
  1321.     }
  1322.     else
  1323.     {     
  1324.         if( dwType != REG_SZ )
  1325.         {
  1326.             _tcsncpy( strDest, strDefault, cchDest );
  1327.             strDest[cchDest-1] = 0;
  1328.             return S_FALSE;
  1329.         }   
  1330.     }
  1331.  
  1332.     return S_OK;
  1333. }
  1334.  
  1335.  
  1336.  
  1337.  
  1338. //-----------------------------------------------------------------------------
  1339. // Name: DXUtil_WriteStringRegKey()
  1340. // Desc: Helper function to write a registry key string
  1341. //-----------------------------------------------------------------------------
  1342. HRESULT DXUtil_WriteStringRegKey( HKEY hKey, LPCTSTR strRegName,
  1343.                                   LPCTSTR strValue )
  1344. {
  1345.     if( NULL == strValue )
  1346.         return E_INVALIDARG;
  1347.         
  1348.     DWORD cbValue = ((DWORD)_tcslen(strValue)+1) * sizeof(TCHAR);
  1349.  
  1350.     if( ERROR_SUCCESS != RegSetValueEx( hKey, strRegName, 0, REG_SZ, 
  1351.                                         (BYTE*)strValue, cbValue ) )
  1352.         return E_FAIL;
  1353.  
  1354.     return S_OK;
  1355. }
  1356.  
  1357.  
  1358.  
  1359.  
  1360.